#pragma once
#include "libzqutil/ustring.h"

#include "net/defines.h"

#ifdef WIN32

#define dll_ex ".dll"
#define open_dll(dll) LoadLibraryA(dll)
#define get_proc(hdll, func) GetProcAddress(hdll, func)
#define close_dll(dll) FreeLibrary((HINSTANCE)dll)
#define dll_error() GetLastError()
#else

#include <dlfcn.h> 

#define dll_ex ".so"
#define open_dll(dll) dlopen(dll, RTLD_LAZY)
#define get_proc(hdll, func) dlsym(hdll, func)
#define close_dll(dll) dlclose(dll)
#define dll_error() dlerror()

#endif // WIN32

namespace libzq
{
	template<typename Handler>
	class MethodMgrImpl
	{
	public:
		virtual shared_ptr<Handler> find_handler(const string& method);
		virtual bool install_handler(const string& method, Handler);
		virtual bool install_handler_map(const map<string, Handler>& handler_map);
		virtual bool uninstall_handler(const string& method);

	private:
		map<string, shared_ptr<Handler>> _handler_map;
	};

	template<typename Handler>
	class DllMethodMgr :
		public MethodMgrImpl<Handler>
	{
	private:
		struct DllModule
		{
			void* _dll;
			vector<string> _method_vec;
		};
		typedef map<string, Handler>(*get_dll_method_map_func_t)(void);
	public:
		bool install_module(const string& module, const string& dll_path);
	protected:
		bool install_module(const string& module, void* dll);
	private:
		map<string, DllModule> _dll_module_map;
	};


	template<typename Handler>
	inline shared_ptr<Handler> MethodMgrImpl<Handler>::find_handler(const std::string & method)
	{
		auto itor = _handler_map.find(method);
		if (itor != _handler_map.end())
		{
			return itor->second;
		}
		return nullptr;
	}

	template<typename Handler>
	inline bool MethodMgrImpl<Handler>::install_handler(const string & method, Handler handler)
	{
		if (find_handler(method))
		{
			LOGE("Duplicated method %s", method);
			return false;
		}
		_handler_map[method] = make_shared<Handler>(handler);
		LOGI("Install method handler of %s", method);
		return true;
	}

	template<typename Handler>
	inline bool MethodMgrImpl<Handler>::install_handler_map(const map<string, Handler>& handler_map)
	{
		for (auto itor = handler_map.begin(); itor != handler_map.end(); ++itor)
		{
			MethodMgrImpl<Handler>::install_handler(itor->first, itor->second);
		}
		return true;
	}

	template<typename Handler>
	inline bool MethodMgrImpl<Handler>::uninstall_handler(const string & method)
	{
		auto itor = _handler_map.find(method);
		if (itor != _handler_map.end())
		{
			_handler_map.erase(itor);
		}
		return true;
	}

	template<typename Handler>
	inline bool DllMethodMgr<Handler>::install_module(const string & module, const string & dll_path)
	{
		auto dll = open_dll(dll_path.c_str());
		if (!dll)
		{
			LOGE("Load dll from %s error %s", dll_path, dll_error());
			return false;
		}
		return install_module(module, dll);
	}

	template<typename Handler>
	inline bool DllMethodMgr<Handler>::install_module(const string & module, void * dll)
	{
		auto func = (get_dll_method_map_func_t)get_proc(dll, "get_module_method");
		if (!func)
		{
			LOGE("Get %s's method error %s", module, dll_error());
			return false;
		}
		auto method_map = func();
		DllModule dll_module{ dll };

		for (auto itor : method_map)
		{
			MethodMgrImpl<Handler>::install_handler(itor.first, itor.second);
			dll_module._method_vec.push_back(itor.first);
		}
		_dll_module_map[module] = dll_module;
		return true;
	}
}